package ca.kooki.codesnippet.core;

import java.util.Stack;

public class XmlBuffer {

  //```````````````````````
  // STATIC VARIABLES
  //```````````````````````

  private static final String XML_VERSION = "<?xml version=\"1.0\"?>";
  private static final String OPEN_START_XML = "<";
  private static final String OPEN_END_XML = "</";
  private static final String CLOSE_EMPTY_XML = "/>";
  private static final String CLOSE_XML = ">";
  
  //```````````````````````
  // MEMBER VARIABLES
  //```````````````````````
  
  private Stack<String> _nestedEntities;
  private boolean _isTagOpen;
  private StringBuffer _currentAttributes;
  private boolean _isEntityEmpty;
  
  private StringBuffer _buffer;

  //```````````````````````
  // CONSTRUCTOR
  //```````````````````````
    
  public XmlBuffer() 
  {
    init();
  }
  
  private void init()
  {
    _isTagOpen = false;
    _nestedEntities = new Stack<String>();
    _currentAttributes = new StringBuffer();
    _buffer = new StringBuffer(XML_VERSION);
    addNewline();
  }

  //```````````````````````
  // INTERFACE
  //```````````````````````
  
  // Convert unsafe xml into safe xml
  static public String safeXml(String unsafeXml) 
  {
    String saferXml = unsafeXml;
    saferXml = saferXml.replaceAll("&", "&amp;");
    saferXml = saferXml.replaceAll("<","&lt;");
    saferXml = saferXml.replaceAll(">","&gt;");
    saferXml = saferXml.replaceAll("'","&apos;");
    saferXml = saferXml.replaceAll("\"","&quot;");
    return saferXml;
  }  

  // Write an entity like <book 
  public void addEntity(String name) 
  {
    closeTagAsRequired(true);
    addIndentation();
    _buffer.append(OPEN_START_XML);
    _buffer.append(name);
    _nestedEntities.add(name);
    _isEntityEmpty = true;
    _isTagOpen = true;
  }
  
  public void addSimpleEntity(String name, String innerXml)
  {
    addEntity(name);
    addInnerXml(innerXml);
    closeEntity();
  }
  
  // write an attribute id="123" in <book id="123" >
  public void addAttribute(String attributeName, String itsValue) 
  {
    _currentAttributes.append(" ");
    _currentAttributes.append(attributeName);
    _currentAttributes.append("=\"");
    _currentAttributes.append(safeXml(itsValue));
    _currentAttributes.append("\"");
  }

  public void addInnerXml(String innerXml) 
  {
    closeTagAsRequired(false);
    _isEntityEmpty = false;
    _buffer.append(safeXml(innerXml));
  }

  public void closeEntity() 
  {
    String name = (String)_nestedEntities.pop();
    
    if (_isEntityEmpty) 
    {
      addAttributesAsRequired();
      _buffer.append(CLOSE_EMPTY_XML);
    } 
    else 
    {
      _buffer.append(OPEN_END_XML);
      _buffer.append(name);
      _buffer.append(CLOSE_XML);
    }
    _isTagOpen = false;
    _isEntityEmpty = false;
    addNewline();
    
  }

  public String toString()
  {
    return _buffer.toString();
  }

  //```````````````````````
  // PRIVATE METHODS
  //```````````````````````

  // write out all current attributes
  private void addAttributesAsRequired() 
  {
    if (_currentAttributes.length() == 0)
    {
      return;
    }

    _buffer.append(_currentAttributes.toString());
    _currentAttributes.setLength(0);
    _isEntityEmpty = false;
  }

  
  // close off the opening tag
  private void closeTagAsRequired(boolean shouldAddNewline)
  {

    if (!_isTagOpen)
    {
      return;
    }
    
    addAttributesAsRequired();
    _buffer.append(CLOSE_XML);
    
    if (shouldAddNewline)
    {
      addNewline();
    }
    
    _isTagOpen = false;
  }
  
  private void addIndentation()
  {
    for (int i=0; i<_nestedEntities.size(); i++)
    {
      _buffer.append("  ");
    }
  }
  
  private void addNewline()
  {
    _buffer.append("\n");
  }
}
